﻿#region License Info
//Component of Cronos Package, http://www.codeplex.com/cronos
//Copyright (C) 2009 Anthony Brockwell

//This program is free software; you can redistribute it and/or
//modify it under the terms of the GNU General Public License
//as published by the Free Software Foundation; either version 2
//of the License, or (at your option) any later version.

//This program is distributed in the hope that it will be useful,
//but WITHOUT ANY WARRANTY; without even the implied warranty of
//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//GNU General Public License for more details.

//You should have received a copy of the GNU General Public License
//along with this program; if not, write to the Free Software
//Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
#endregion


using System;
using System.Collections.Generic;
using ABMath.ModelFramework.Data;
using MathNet.Numerics.Distributions;
using MathNet.Numerics.LinearAlgebra;
using ABMath.IridiumExtensions;


namespace ABMath.ModelFramework.Visuals
{
    [Serializable]
    public class StrategyAnalysisVisual : Visual
    {
        public double SharpeRatio { get; protected set; }
        public double SharpeUtility { get; protected set; }
        public double Kurtosis { get; protected set; }
        public double DrawDown { get; protected set; }

        public TimeSeries TheTimeSeries
        {
            get { var ts = TheObject as TimeSeries;
            return ts; }
        }

        public override string GetDescription()
        {
            return "Analysis of cumulative profits and losses of a strategy";
        }

        public override string GetShortDescription()
        {
            return "P&L";
        }

        private static DateTime DayAdvance(DateTime start)
        {
            var next = start.AddDays(1);
            while (next.DayOfWeek == DayOfWeek.Saturday || next.DayOfWeek == DayOfWeek.Sunday)
                next = next.AddDays(1);
            return next;
        }

        // compute realized Sharpe ratio, skipping weekends, by chopping into daily values, assuming zero risk-free gain
        // the input is cumulative P&L
        private void Recompute(TimeSeries ts)
        {
            DateTime dt1 = ts.TimeStamp(0);
            DateTime dt2 = ts.GetLastTime();

            var diffs = new List<double>(500);
            DateTime current = dt1;
            DateTime next = DayAdvance(current);
            while (next <= dt2)
            {
                double x2 = ts.ValueAtTime(next);
                double x1 = ts.ValueAtTime(current);
                diffs.Add(x2 - x1);
                current = next;
                next = DayAdvance(next);
            }

            var v = new Vector(diffs.Count);
            int numTrades = 0;
            for (int i = 0; i < diffs.Count; ++i)
            {
                v[i] = diffs[i];
                if (v[i] != 0)
                    ++numTrades;
            }

            double std = Math.Sqrt(v.Variance());
            double mn = v.Mean();

            SharpeRatio = Math.Sqrt(250)*mn/std;
            Kurtosis = v.Kurtosis() - 3;
            DrawDown = v.Integrate().MaxDrawDown();

            // compute utility = E[g(SR)], g(x)=sign(x)*x^2, under the assumption of normally distributed gains
            //double utility = 0.0;
            //if (numTrades < 2)
            //    numTrades = 2;
            //var stdn = new StudentsTDistribution(numTrades);
            //for (int k=0 ; k<10000 ; ++k)
            //{
            //    double draw = stdn.NextDouble();
            //    utility += Utility(SharpeRatio + draw/Math.Sqrt(numTrades));
            //}
            //SharpeUtility = utility/10000.0;
        }

        private double Utility(double x)
        {
            return x*x*Math.Sign(x);
        }

        protected override bool OnInputChanged()
        {
            var ts = TheObject as TimeSeries;
            if (ts == null)
                return false;

            // recompute everything
            Recompute(ts);
            if (associatedControl != null)
                associatedControl.DataHasChanged();
            return true;
        }

        public override List<Type> GetAllowedInputTypesFor(int socket)
        {
            if (socket != 0)
                throw new SocketException();
            return new List<Type> { typeof(TimeSeries) };
        }

        public override List<Type> GetOutputTypesFor(int socket)
        {
            if (socket != 0)
                throw new SocketException();
            return new List<Type>();
        }

    }
}
